﻿using System;
using System.Collections.Generic;
using System.Xml;
using System.Windows.Forms;

using System.Text;
using SdmxMl.Common;
using SdmxMl.Helper;

namespace SdmxMl.Structure
{
    public enum CrossSectionalAttachKind
    {
        None,
        DataSet,
        Group,
        Section,
        Observation,
    }

    public class DimensionType : TimeDimensionType
    {
        public static string SdmxInfo =
            "DimensionType describes the structure of non-Time dimensions. The order of their declaration" +
            "is significant: it is used to describe the order in which they will appear in data formats for" +
            "which key values are supplied in an ordered fashion (exclusive of the Time dimension, which is " +
            "not represented as a member of the ordered key). Some types of non-Time  dimensions have " +
            "un-coded values: the TextFormat element must be provided, to indicate what type of values are " +
            "permissible. The attributes isFrequencyDimension and isEntityDimension may have a 'true' value " +
            "for any instance of the Dimension element, indicating that it is a dimension of the stated type. " +
            "The attributes isCountDimension, isNonObservationalTimeDimension, isMeasureDimension, and is " +
            "IdentityDimension may occur multiple times, and take a true value to indicate that the diemsnion " +
            "in question is of that type. Note that only one dimension in the key family may be of the " +
            "following types: Frequency dimension and Entity dimension, and only if there is not also an " +
            "attribute in the key family of the same type. Any given dimension may only have a true value " +
            "for one of the set of attributes isFrequencyDimension, isCountDimension, is measureDimension,  " +
            "isEntityDimension, isNonObservationalTimeDimension, and is IdentityDimension. " +
            "The definitions and limits on representation of each dimension type are as follows: Frequency " +
            "dimension describes the period between observations, and is coded; Count dimensions are " +
            "represented by values which are sequential, incrementing numbers - representations are always " +
            "of the Increment or Count type; measureType dimensions are always coded, and they enumerate " +
            "the set of possible measures declared for the key family; Entity dimensions describe the subject " +
            "of the data set (ie, a country) - they can be coded or represented in any other form; " +
            "Non-Observational Time dimensions must have a representation which contains a time; Identity " +
            "dimensions may be coded or uncoded, but must be represented by a scheme which refers to the " +
            "identifiers of external entites. (Conventionally, it is the first dimension in the ordered set " +
            "of dimensions - the key.) If a key family describes cross-sectional data, then for each " +
            "dimension, the crossSectionalAttachDataSet, crossSectionalAttachGroup, " +
            "crossSectionalAttachSection, and crossSectionalAttachObservation attributes must be given values. " +
            "A value of 'true' for any of these attributes indicates that the dimension may be provided " +
            "a value at the indicated level within the cross-sectional structure. Note that these attributes " +
            "do not need to be provided for any dimension with the isFrequencyDimension set to 'true, as " +
            "these dimensions are always attached only at the group level, as is time. A key family " +
            "designed for cross-sectional use must be structured such that any observation's complete " +
            "key can be unambiguously described by taking each dimension value from its observation level, " +
            "section level, group level, and data set level, and ordered according to the sequence given " +
            "in the key family.  For any dimension, the id of the referenced concept must be unique " +
            "across the entire key family, including all dimensions, attributes and measures.";

        public enum DimensionKind
        {
            Dimension,
            Measure,
            Frequency,
            Entity,
            Count,
            NonObservationTime,
            Identity,
            // Not real
            TimeDimension,
            PrimaryMeasure
        }

        public DimensionType()
            : base()
        {
            Init();
        }

        private void Init()
        {
            _dimKind = DimensionKind.Dimension;
        }

        public DimensionType(XmlNode nodeArtefact, SdmxNamespaceManager nsmgr) 
            : base(nodeArtefact, nsmgr)
        { 
            Init();
            foreach (DimensionKind k in Enum.GetValues(typeof(DimensionKind)))
            {
                string kind = Xmlhelper.GetAtt(nodeArtefact, "is" + k.ToString() + "Dimension");
                if (kind.ToLower() == "true")
                {
                    _dimKind = k;
                    break;
                }
            }
            
        }


        public override void WriteXml(XmlTextWriter xw, string prefix, string tag)
        {
            xw.WriteStartElement(prefix, tag, null);

            if (SdmxNamespaceManager.SdmxTarget == SdmxVersion.SDMX_20)
            {
                WriteReferences(xw, prefix);
                if (_dimKind != DimensionKind.Dimension)
                    xw.WriteAttributeString("is" + _dimKind.ToString() + "Dimension", "true");
                writeCrossSectioalAttach(xw);

                // Text Format
                if (textFormat.TextType != TextTypeType.Undefined)
                    textFormat.WriteXml(xw, prefix, "TextFormat");

                // Support new SDMX 2.1 ID attribute as Annotations
                WriteAnnotationsAndId(xw, prefix);

            }
            else // 2.1
            {
                WriteReferences(xw, prefix);
            }

            xw.WriteEndElement();
        }


        private DimensionKind _dimKind;
        public DimensionKind DimKind
        {
            get { return _dimKind; }
            set { _dimKind = value; }
        }

        public override string ToString()
        {
            string txt = base.ToString();
            if (_dimKind != DimensionKind.Dimension)
                txt += " - " + _dimKind.ToString();
            return txt;
        }

    }


    public class DimensionTypeList : BaseDimensionTypeList
    {
        public DimensionTypeList(KeyFamilyType dsd)
            : base(dsd)
        {
        }

        public override string ToString()
        {
            return "Dimensions";
        }

        public override string GetCrossAttachText(BaseDimensionType bd)
        {
            if (bd is DimensionType)
                return (bd as DimensionType).CrossSectionalAttach.ToString();
            else
                return (bd as TimeDimensionType).CrossSectionalAttach.ToString();
        }

        public override DimensionType.DimensionKind GetKind(BaseDimensionType bd)
        {
            if (bd is DimensionType)
                return (bd as DimensionType).DimKind;
            else
                return DimensionType.DimensionKind.TimeDimension;

        }

        public override BaseDimensionType NewItem()
        {
            return new DimensionType();
        }

        /// <summary>
        /// Only dimension of base type dimension, no other role
        /// </summary>
        /// <returns></returns>
        public List<string> GetDimensionRoleList()
        {
            // mainly dimensions role so alloc full size
            List<string> ld = new List<string>(this.Count);
            foreach (BaseDimensionType dim in this)
               if (dim != null)
                    ld.Add(dim.ConceptInfo.Id);

            return ld;
            
        }

        public List<DimensionType> GetMeasureDimensions()
        {
            List<DimensionType> listMeasure = new List<DimensionType>();

            foreach (BaseDimensionType dim in this)
            {
                DimensionType aDim = dim as DimensionType;
                    if (aDim != null && aDim.DimKind == DimensionType.DimensionKind.Measure)
                        listMeasure.Add(aDim);
            }
            return listMeasure;
       }

        public TimeDimensionType GetTimeDimension()
        {
            foreach (BaseDimensionType bdim in this)
            {
                DimensionType aDim = bdim as DimensionType;
                if (aDim == null && bdim is TimeDimensionType)
                    return bdim as TimeDimensionType;
            }
            return null;
        }

    }
}


